﻿using System.Collections;
using System.ComponentModel;
using System.Diagnostics;
using System.Net;
using System.Net.Sockets;
using System.Reflection;
using log4net;
using Newtonsoft.Json.Linq;
using Bzstd.Utility.Assertion;
using Microsoft.VisualBasic.FileIO;

namespace Bzstd.Utility;

/// <summary>
/// 公用工具类
/// </summary>
public static class CommonUtility
{
	/// <summary>
	/// 获取根源异常
	/// </summary>
	/// <param name="e"></param>
	/// <returns></returns>
	public static Exception GetRootException(this Exception e)
	{
		while (true)
		{
			var root = e.InnerException;
			if (root == null || e is AssertionException)
			{
				return e;
			}
			e = root;
		}
	}

	/// <summary>
	/// 获取Trimmed异常
	/// </summary>
	/// <param name="e"></param>
	/// <returns></returns>
	public static Exception GetTrimmedException(this Exception e)
	{
		while (e is TargetInvocationException or AggregateException)
		{
			e = e.InnerException;
		}
		return e;
	}

	/// <summary>
	/// 打印异常
	/// </summary>
	/// <param name="log"></param>
	/// <param name="exception"></param>
	/// <param name="message"></param>
	public static void LogException<T>(this ILogger<T> log, Exception exception, string message = null)
	{
		var (msg, ex) = (exception.GetTrimmedException(), exception.GetRootException()) switch
		{
			(_, AssertionException r) => (r.Message, null),
			(_, EndOfStreamException r) => (r.Message, null),
			(_, SocketException r) => (r.Message, null),
			(Exception e, Exception r) => (r.Message, e)
		};
		log.LogError(ex, "{Message}", message + msg);
	}

	/// <summary>
	/// 打印异常
	/// </summary>
	/// <param name="log"></param>
	/// <param name="exception"></param>
	/// <param name="message"></param>
	public static void LogException(this ILog log, Exception exception, string message = null)
	{
		var (msg, ex) = (exception.GetTrimmedException(), exception.GetRootException()) switch
		{
			(_, AssertionException r) => (r.Message, null),
			(_, EndOfStreamException r) => (r.Message, null),
			(_, SocketException r) => (r.Message, null),
			(Exception e, Exception r) => (r.Message, e)
		};
		log.Error(message + msg, ex);
	}

	/// <summary>
	/// 转换类型
	/// </summary>
	/// <param name="obj"></param>
	/// <param name="type"></param>
	/// <returns></returns>
	public static object ChangeType(object obj, Type type)
	{
		if (obj == null || type.IsInstanceOfType(obj))
		{
			return obj;
		}
		return JToken.FromObject(obj).ToObject(type);
	}

	/// <summary>
	/// 转换类型
	/// </summary>
	/// <param name="obj"></param>
	/// <typeparam name="T"></typeparam>
	/// <returns></returns>
	public static T ChangeType<T>(object obj)
	{
		return (T)ChangeType(obj, typeof(T));
	}

	/// <summary>
	/// 字符串转IPEndPoint
	/// </summary>
	/// <param name="value"></param>
	/// <returns></returns>
	public static IPEndPoint ParseIPEndPoint(string value)
	{
		if (string.IsNullOrWhiteSpace(value))
		{
			return null;
		}
		string[] address = value.Split(':');
		return new IPEndPoint(IPAddress.Parse(address[0]), Convert.ToUInt16(address[1]));
	}

	/// <summary>
	/// WaitWhile
	/// </summary>
	/// <param name="condition"></param>
	/// <returns></returns>
	public static async Task WaitWhile(Func<bool> condition)
	{
		while (condition())
		{
			await Task.Delay(TimeSpan.FromSeconds(0.1));
		}
	}

	/// <summary>
	/// WaitUntil
	/// </summary>
	/// <param name="condition"></param>
	/// <returns></returns>
	public static async Task WaitUntil(Func<bool> condition)
	{
		while (!condition())
		{
			await Task.Delay(TimeSpan.FromSeconds(0.1));
		}
	}

	/// <summary>
	/// ForEach
	/// </summary>
	/// <param name="enumerable"></param>
	/// <param name="action"></param>
	/// <typeparam name="T"></typeparam>
	public static void ForEach<T>(this IEnumerable<T> enumerable, Action<T> action)
	{
		foreach (var item in enumerable)
		{
			action?.Invoke(item);
		}
	}

	/// <summary>
	/// ForEach
	/// </summary>
	/// <param name="enumerable"></param>
	/// <param name="action"></param>
	/// <typeparam name="T"></typeparam>
	public static void ForEach<T>(this IEnumerable<T> enumerable, Action<T, int> action)
	{
		int i = 0;
		foreach (var item in enumerable)
		{
			action?.Invoke(item, i++);
		}
	}

	/// <summary>
	/// 在资源管理器中显示文件
	/// </summary>
	/// <param name="path"></param>
	[Description("在资源管理器中显示文件")]
	public static void ShowInExplorer([Category("String"), Description("文件路径")] string path)
	{
		Process.Start("explorer", "/select," + path);
	}

	/// <summary>
	/// 获取资源字符串
	/// </summary>
	/// <param name="path"></param>
	/// <returns></returns>
	public static string GetResourceString(string path)
	{
		using var stream = GetResourceStream(path);
		using var reader = new StreamReader(stream);
		return reader.ReadToEnd();
	}

	/// <summary>
	/// 获取资源字节数组
	/// </summary>
	/// <param name="path"></param>
	/// <returns></returns>
	public static byte[] GetResourceBytes(string path)
	{
		using var stream = GetResourceStream(path);
		using var memoryStream = new MemoryStream();
		stream.CopyTo(memoryStream);
		return memoryStream.ToArray();
	}

	/// <summary>
	/// 获取资源流
	/// </summary>
	/// <param name="path"></param>
	/// <returns></returns>
	public static Stream GetResourceStream(string path)
	{
		return ConfigHelper.ProductAssembly.GetManifestResourceStream($"{ConfigHelper.ProductNamespace}.Resources.{path}");
	}

	/// <summary>
	/// IEnumerator转IEnumerable
	/// </summary>
	/// <param name="enumerator"></param>
	/// <returns></returns>
	public static IEnumerable ToEnumerable(this IEnumerator enumerator)
	{
		while (enumerator.MoveNext())
		{
			yield return enumerator.Current;
		}
	}

	/// <summary>
	/// IEnumerator转IEnumerable
	/// </summary>
	/// <param name="enumerator"></param>
	/// <returns></returns>
	public static IEnumerable<T> ToEnumerable<T>(this IEnumerator enumerator)
	{
		while (enumerator.MoveNext())
		{
			if (enumerator.Current is T current)
			{
				yield return current;
			}
		}
	}

	/// <summary>
	/// IEnumerator转IEnumerable
	/// </summary>
	/// <param name="enumerator"></param>
	/// <typeparam name="T"></typeparam>
	/// <returns></returns>
	public static IEnumerable<T> ToEnumerable<T>(this IEnumerator<T> enumerator)
	{
		while (enumerator.MoveNext())
		{
			yield return enumerator.Current;
		}
	}

	/// <summary>
	/// 删除文件到回收站
	/// </summary>
	public static void DeleteFileToRecycleBin(string path)
	{
		FileSystem.DeleteFile(path, UIOption.OnlyErrorDialogs, RecycleOption.SendToRecycleBin, UICancelOption.DoNothing);
	}
}
